/*
 * Created on 8. des.. 2006
 * Created by Tomas
 */
package gui;

import gui.Presenter.PersonSelectionListener;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.PatternSyntaxException;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import people.Person;
import people.PersonNotFoundException;
import placing.Mapper;
import placing.Place;


/**
 * 
 * Presents the data from a MembershipManager as a JTable in a JPanel.
 * 
 * @author Tomas Ekeli
 *
 */
public class PersonsOverviewPanel extends JPanel {
	private static final long serialVersionUID = 1L;
	private Mapper map;
	
	private final int WIDTH = 200;
	private final int HEIGHT = 500;
	
//	private final String filterLabelString = "Search";
	private final String filterTextFieldHint = "enter search RegEx";
	
	private Presenter parent;
	private PersonsTableModel model = new PersonsTableModel();
	private JTable table;
	
	private List<PersonSelectionListener> selectionListeners;
	
	public PersonsOverviewPanel(Presenter p) {
		super(new BorderLayout());

		parent = p;
		map = p.getCurrentMapper();
		
		selectionListeners = new LinkedList<PersonSelectionListener>();
		
		//create the table
		table = new JTable(model);
		table.setPreferredScrollableViewportSize(new Dimension(WIDTH, HEIGHT));

		//Make the table sort
		TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);
		table.setRowSorter(sorter);
		
		//add a filter with a button and an inputfield to this panel
		JPanel filterInput = makeFilterPanel(sorter);
		
		//set the width of the columns
		setColumnWidths(table);
		
		//set the selection-behaviour
		setSelectionBehaviour(table);
		
		//set up any particular rendering/editing behaviour
		
		//create the scroll pane and add the table to it
		JScrollPane scrollPane = new JScrollPane(table);
		
		//add the scroll pane to this panel
		add(filterInput, BorderLayout.NORTH);
		add(scrollPane, BorderLayout.CENTER);
	}
	
	public JTable getTable() {
		return table;
	}

	private JPanel makeFilterPanel(final TableRowSorter<TableModel> sorter) {
		JPanel filterPanel = new JPanel(new BorderLayout());
		
		final JTextField filterTextField = new JTextField(filterTextFieldHint);
//		JButton filterButton = new JButton(filterLabelString);
		
		filterTextField.addCaretListener(new CaretListener() {

			public void caretUpdate(CaretEvent e) {
				String text = filterTextField.getText();
				if (text.length() == 0 || text.equals(filterTextFieldHint)) {
					sorter.setRowFilter(null);
				} else {
					try {
						//do a case-insensitive search (?i)
						sorter.setRowFilter(RowFilter.regexFilter("(?iu)"+text));
						
					} catch (PatternSyntaxException e1) {
						//just wait untill it's well-formed. 
					}
				}
				
			}

			
		});

		filterPanel.add(filterTextField, BorderLayout.CENTER);
//		filterPanel.add(filterButton, BorderLayout.EAST);
		int b = 4;
		filterPanel.setBorder(BorderFactory.createEmptyBorder(b, b, b*3, b));
		
		return filterPanel;
	}


	/*
	 * Sets the column widths of all other columns than the name to 5 characters,
	 * the name-column will take up the remaining space
	 */
	private void setColumnWidths(JTable table) {
//		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		
		PersonsTableModel tableModel = (PersonsTableModel) table.getModel();
		TableColumnModel columnModel = table.getColumnModel();
		
		//Renderers used to make components
		TableCellRenderer headerRenderer = table.getTableHeader().getDefaultRenderer();
		TableCellRenderer cellRenderer = null;
		
		TableColumn column = null;
		Component component = null;
		
		int headerWidth = 0;
		int cellWidth = 0;
		
		//Calculate the width of the current column
		for (int i = 0; i < columnModel.getColumnCount(); i++) {
			column = columnModel.getColumn(i);
			
			//The width of the header
			component = headerRenderer.getTableCellRendererComponent(table,
					column.getHeaderValue(), false, false, 0, 0);
			headerWidth = component.getPreferredSize().width;
			
			//Make the cell renderer based on the type of cell
			cellRenderer = table.getDefaultRenderer(column.getClass());
			
			//The width of the cell, asks the tableModel for a long value
			component = cellRenderer.getTableCellRendererComponent(table, 
					tableModel.getLongvalueAt(i), false, false, 0, i);
			cellWidth = component.getPreferredSize().width;
			
			column.setPreferredWidth( (headerWidth > cellWidth) ? headerWidth : cellWidth);
		}
		
		table.doLayout();
	}
	
	/*
	 * Sets the selection behaviour to only allow selection of a single row
	 * and to print out when a new row is selected.
	 */
	private void setSelectionBehaviour(JTable tab) {
		final JTable t = tab;
		
		//The list of persons selected
		final LinkedList<Person> selection = new LinkedList<Person>();
		
		//ask to be notified of selection changes
		ListSelectionModel rowSM = t.getSelectionModel();
		
		//Add a small behaviour on selection
		rowSM.addListSelectionListener(new ListSelectionListener() {

			public void valueChanged(ListSelectionEvent e) {
				//ignore extra messages
				if (e.getValueIsAdjusting()) {
					return;
				}
				ListSelectionModel lsm = (ListSelectionModel) e.getSource();
				if (lsm.isSelectionEmpty()) {
					//no rows are selected
				} else {
					selection.clear();
					int minRow = table.convertRowIndexToModel(lsm.getMinSelectionIndex());
					int maxRow = table.convertRowIndexToModel(lsm.getMaxSelectionIndex());
					System.out.println("Selected from " +
							t.getModel().getValueAt(minRow, 1) + " in row " 
							+ minRow + " to " +
							t.getModel().getValueAt(maxRow, 1) + " in row "
							+ maxRow);	
					
					for (int i = minRow; i <= maxRow; i++) {
						try {
							selection.add(map.getPerson((Integer)t.getModel().getValueAt(i, 0)));
						} catch (PersonNotFoundException e1) {
							// did not find the person in the selection
							e1.printStackTrace();
						}
					}
					
					for (PersonSelectionListener listener : selectionListeners) {
						listener.selectionChanged(selection);
					}
				}
				
			}
			
		});
	}
	
	public void addSelectionListener(PersonSelectionListener listener) {
		selectionListeners.add(listener);
	}


	public void removeSelectionListener(ListSelectionListener listener) {
		selectionListeners.remove(listener);
	}

	/**
	 * 
	 * The model which controls how the Persons are presented. This model
	 * is hardcoded to display 4 columns: ID, Name, Place and Score;
	 * 
	 * @author Tomas Ekeli
	 *
	 */
	private class PersonsTableModel extends AbstractTableModel {
		private static final long serialVersionUID = 1L;
		
		private String[] colNames = {"ID", "Name", "Place", "Score"};
		private String[] colMax = {"12333", "Rowan Atkinson", "123-123", "14235"};
		
		public String getLongvalueAt(int columnIndex) {
			return colMax[columnIndex];
		}

		public int getColumnCount() {
			return colNames.length;
		}

		public int getRowCount() {
			return map.getNumPersons();
		}
		
		@Override
		public void fireTableDataChanged() {
			System.err.println("Fire!");
			map = parent.getCurrentMapper();
			super.fireTableDataChanged();
		}

		public Object getValueAt(int rowIndex, int columnIndex) {
			Person p = map.getPersons().get(rowIndex);
			
			Object returnMe;
			
			switch (columnIndex) {
				case 0:
					returnMe = p.getID();
					break;
				case 1:
					returnMe = p.getName();
					break;
				case 2:
					Place pl = p.getPlace();
					if (pl != null) {
						returnMe = p.getPlace().toString();
					}
					else {
						returnMe = "N/A";
					}
					break;
				case 3:
					returnMe =  p.getScore();
					break;
				case 4:
					returnMe =  p.getComment();
					break;
				default:
					return null;
			}
			
			return returnMe;
		}
		
		@Override
		public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
			try {
				Person p = map.getPerson((Integer)getValueAt(rowIndex, 0));
			
				switch (columnIndex) {
					//the value edited is the ID
					case 0: 
						System.err.println("Cannot change the ID of a person.");
						return;
					//the value edited is the name
					case 1:
						System.err.println("Cannot change the name of a person");
						return;
					//the value edited is the place
					case 2: //TODO: should be possible to assign places manually
						System.err.println("Not possible to assign manually");
						return;
					//the value edited is the score
					case 3:
						p.setScore((Integer)aValue);
						return;
					//the value edited is the comment
					case 4:
						p.setComment((String) aValue);
					default:
						break;
				}
				
			} catch (PersonNotFoundException e) {
				// The person was not found. Editing empty row?
				System.err.println("Person " + aValue + 
						" not found on edit of " + rowIndex + ", " + columnIndex);
			}
			
			
			//if the value edited is the Place - try to assign at place entered
		}
		
		@Override
		public boolean isCellEditable(int rowIndex, int columnIndex) {
			/*
			 * For now, the score and comment are the only editable values
			 */
			return (columnIndex > 2);
		}
		
		/**
		 * Used by the table to select a suitable renderer
		 */
		@Override
		public Class<?> getColumnClass(int columnIndex) {
			return getValueAt(0, columnIndex).getClass();
		}
		
		@Override
		public String getColumnName(int column) {
			return colNames[column];
		}
		
	}

}
